package ext.vpde.cadvaulting.pdf;
/***
 * A collection of utilities for managing the creation of PDF files associated with Windchill objects.
 * @date 10SEP08
 ***/

import ext.vpde.cadvaulting.util.FtpUtil;
import ext.vpde.cadvaulting.util.WpHelperUtility;
// Java imports
import java.beans.PropertyVetoException;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.Random;
import java.util.Vector;

//Windchill imports
import wt.content.*;
import wt.epm.EPMDocument;
import wt.epm.EPMDocumentType;
import wt.epm.familytable.EPMSepFamilyTable;
import wt.epm.structure.*;
import wt.fc.*;
import wt.method.RemoteAccess;
import wt.method.RemoteMethodServer;
import wt.org.WTPrincipal;
import wt.maturity.*;
import wt.pom.Transaction;
import wt.queue.*;
import wt.representation.*;
import wt.session.*;
import wt.util.WTException;
import wt.util.WTProperties;
import wt.vc.baseline.Baselineable;
import wt.vc.config.LatestConfigSpec;
import wt.viewmarkup.DerivedImage;

//third party imports
import com.lowagie.text.Element;
import com.lowagie.text.pdf.*;
import com.lowagie.text.*;

public class PDFUtility implements RemoteAccess{

  public static final boolean VERBOSE;
  private static final String WTHOME;
  private static final String WTTEMP;
  private static final String TEMP_DIR;
  public static final boolean DEL_TEMP;
  private static final int MAX_PS_GEN;
  private static final int MAX_PDF_GEN;
  private static final int SLEEP_INTERVAL = 1000;

  public static final String IA_FTP_SERVER;
  public static final String IA_FTP_SERVER_DIR;
  private static final String IA_USER;
  private static final String IA_PWD;

  static {
	  try {
		  WTProperties properties = WTProperties.getLocalProperties();
		  VERBOSE = properties.getProperty("ext.vpde.cadvaulting.pdfgen.verbose", true);
		  WTHOME = properties.getProperty("wt.home");
		  WTTEMP = properties.getProperty("wt.temp");
		  DEL_TEMP = properties.getProperty("ext.vpde.cadvaulting.del_temp_dir", true);
		  TEMP_DIR = properties.getProperty("ext.vpde.cadvaulting.temp_dir", WTTEMP);
		  MAX_PS_GEN = Integer.parseInt(properties.getProperty("ext.vpde.cadvaulting.max_ps_gen", "300"));
		  MAX_PDF_GEN = Integer.parseInt(properties.getProperty("ext.vpde.cadvaulting.max_pdf_gen", "2880"));

		  IA_FTP_SERVER = properties.getProperty("ext.vpde.cadvaulting.ftp_server.in_approval");
		  IA_FTP_SERVER_DIR = properties.getProperty("ext.vpde.cadvaulting.ftp_server_dir.in_approval");
		  IA_USER = properties.getProperty("ext.vpde.cadvaulting.ftp_user.in_approval");
		  IA_PWD = properties.getProperty("ext.vpde.cadvaulting.ftp.pwd.in_approval");
	  }
	  catch (Throwable t) {
		  System.err.println("Error initializing ");
		  t.printStackTrace(System.err);
		  throw new ExceptionInInitializerError(t);
	  }
   }

  private static File tempDir;
  private static File content;
  private static String tempDirPath;

/**************************************************************************
 * Name:        main
 *
 * Purpose:	main method for testing purposes.
 * @param arg[0]: the oid of content holder
 * @param arg[1]  the name of file containing watermark information, located at
 *                <$WT_HOME>/codebase/ext/vpde/cadvaulting/pdf
 * @param arg[2]: true/false to ftp file to designated server
 * Example usage:
 *    java ext.vpde.cadvaulting.pdf.PDFUtility "OR:wt.epm.EPMDocument:160438" NR_watermark
 *
 **************************************************************************
*/
   public static void main(String args[]){
	   try{
		   //preparePDF(args[0], args[1]);
		   //File pdfFile = new File("D:/ptc/Windchill_9.0/Windchill/temp/genpdf_38152/pdm-300-0001_drw.pdf");
		   //String watermark = args[0];
		   //addWatermark(pdfFile, watermark);
		   //OR:wt.epm.EPMDocument:160438
		   //"OR:wt.epm.EPMDocument:240421"
		   //wt.iba.value.IBAHolder ibaholder = (wt.iba.value.IBAHolder)WpHelperUtility.getObject(args[0]);
		   //WpHelperUtility.updatePdfUrl(ibaholder, args[1], args[2], "com.whirlpool.PDF_URL" );
		   //System.exit(0);
		   //wt.maturity.PromotionNotice pn = (wt.maturity.PromotionNotice)WpHelperUtility.getObject(args[0]);
		   deleteContent(args[0], args[1]);
		   //WpHelperUtility.findPNMembers(pn);
	   }catch(Exception e){e.printStackTrace();}
   }

 /**************************************************************************
  * Name:        processPNDrawings
  *
  * Purpose: finds all epm drawings in a Promotion Notice and starts processing
  *          for watermark and ftp
  *
  **************************************************************************
*/
 public static Vector processPNDrawings (PromotionNotice promotionNotice, String watermark)throws Exception {
	  QueryResult members = MaturityHelper.service.getBaselineItems(promotionNotice);
	  EPMDocument epmdoc;
	  String pnid = "" + PersistenceHelper.getObjectIdentifier(promotionNotice);
	  Vector<EPMDocument> drawings = new Vector<EPMDocument>();
	  while (members.hasMoreElements()) {
		  WTObject object = (WTObject)members.nextElement();
		  if (object instanceof EPMDocument ){
			  epmdoc = ((EPMDocument)object);
			  if ( (epmdoc.getDocType().toString()).equals("CADDRAWING") ) {
				  drawings.add(epmdoc);
				  System.out.println("Found in PN: " + object.getDisplayIdentity() );
				  processPdf(epmdoc,pnid,watermark,true,false);
			  }
		  }
	  }
	  return drawings;
  }

/**************************************************************************
 * Name:        createDirectory
 *
 * Purpose:	create a directory on server.  If prefix inculded, will generate a
 *          random name following given prefix..
 *
 **************************************************************************
*/
  public static File createDirectory(String prefix, String basepath) throws java.io.IOException {
	File temp = null;
	if (prefix==null || prefix.equals(""))
       temp = new File(basepath);
    else
       temp= File.createTempFile(prefix,"", new File(basepath));
	temp.delete();
    temp.mkdir();
    return temp;
  }

  public static int getMaxPDFGen(){
	  return MAX_PDF_GEN;
  }

/**************************************************************************
 * Name:        processPdf
 *
 * Purpose:	executes all required methods for generating a pdf file
 *
 **************************************************************************
*/
  public static String processPdf(String epmoid, String pnoid, String watermark) throws Exception {
	  EPMDocument epmdoc = (EPMDocument)WpHelperUtility.getObject(epmoid);
	  return processPdf(epmdoc,pnoid, watermark,true,false);
  }

  public static String processPdf(EPMDocument epmdoc,String pnid, String watermark, boolean ftp) throws Exception {
	  return processPdf(epmdoc,pnid, watermark, ftp, (ftp==false)?true:false );
  }
  public static String processPdf(EPMDocument epmdoc,String pnid, String watermark, boolean ftp, boolean attach) throws Exception {
	  File pdfFile = null;
	  String ftpPath;
	  if (VERBOSE) System.out.println ("EPMDocument type = " + epmdoc.getDocType().toString() );
	  if ( (epmdoc.getDocType().toString()).equals("CADDRAWING") ) {
	    tempDir = createDirectory("genpdf_",TEMP_DIR);
	    tempDirPath = tempDir.getAbsolutePath();
	    if (VERBOSE) System.out.println("*** Created temp dir: "         + tempDirPath +
	                                  "\n*** Promotion Notice id: " + pnid +
		                              "\n*** Processing EPM document:\t" + ((WTObject)epmdoc).getDisplayIdentity() );
	    //Download content of given epmdocument
	    downloadRepresentation((Representable)epmdoc,pnid);

	    PSFilter filter = new PSFilter("pdf");
	    File[] list = tempDir.listFiles(filter);
	    //monitor directory for download of pdf file, continue when present
	    int i = 0;
	    while ( list.length==0 || !list[0].exists() || !list[0].canWrite()){
		   Thread.currentThread().sleep(SLEEP_INTERVAL);
		   if (VERBOSE) System.out.print(".");
		   if (i++>=MAX_PS_GEN) throw new WTException("***** ERROR: PDF download exceeded allowable time limit ****");
		   list = tempDir.listFiles(filter);
	    }

	    for ( i = 0; list != null && i < list.length; i++){
			pdfFile = list[i];
			//upload pdf as secondary content to epmdoc without watermark
			//

			if (watermark!=null && !watermark.equals("")){
				if (VERBOSE) System.out.println("Inserting watermark: " + watermark );
				addWatermark( pdfFile, watermark );
			}
			if ( (ftp==true) &&IA_FTP_SERVER_DIR!=null && !IA_FTP_SERVER_DIR.equals("")){
				ftpPath = IA_FTP_SERVER_DIR + pdfFile.getName();
				if (VERBOSE) System.out.println("\nTransfering " + pdfFile.getName() + " to " + IA_FTP_SERVER );
				FtpUtil.upload(IA_FTP_SERVER, IA_USER, IA_PWD, ftpPath, pdfFile);
			}else if (VERBOSE) System.out.println("**** FTP NOT ENABLED ****");
			if (attach==true) uploadContent(pdfFile, epmdoc, false);
	    }
	    if (DEL_TEMP) removeDirectory(tempDir);
      }
      return (pdfFile==null)?"":pdfFile.getName();
  }

/**************************************************************************
 * Name:        downloadRepresentation
 *
 * Purpose:	Invokes writing of content data to disk.
 *
 **************************************************************************
*/
  public static File downloadRepresentation ( Representable representable, String name) throws Exception, java.io.IOException, InvocationTargetException {

      String repName=null;
      Representation representation = null;
      File theFile = null;

      QueryResult reps = RepresentationHelper.service.getRepresentations(representable);
	  while (reps.hasMoreElements()) {
		  representation = (Representation)reps.nextElement();
		  repName = representation.getName();
		  if (repName.equals(name)){
			  if (VERBOSE) System.out.println("Found rep: " + repName );
			  ContentHolder contentcookie = ContentHelper.service.getContents(representation);
			  ContentRoleType role = ContentRoleType.toContentRoleType("SECONDARY");
			  QueryResult repSecondary =  ContentHelper.service.getContentsByRole(representation, role);
			  while (repSecondary.hasMoreElements()) {
				  ApplicationData appdata = (ApplicationData)repSecondary.nextElement();
				  repName = appdata.getFileName();
				  if (repName.endsWith("_drw.pdf")){
					  repName = repName.substring(0,repName.indexOf("_drw.pdf")) + ".pdf";
					  theFile = new File(tempDir, repName);
					  if (VERBOSE) System.out.println("contents by role: " + repName );
					  ContentServerHelper.service.writeContentStream(appdata, theFile.getAbsolutePath() );
				  }
			  }
		  }
	  }
      return theFile;
  }

/**************************************************************************
 * Name:        uploadContent
 *
 * Purpose:	upload content files from disk to contentholder's as primary or
 *          secondary (attachment).
 *
 **************************************************************************
*/
    public static void uploadContent(File contentFile, ContentHolder contentholder, boolean isPrimary)
        throws WTException, FileNotFoundException, java.beans.PropertyVetoException, IOException
    {
		boolean isNew=false;
		if ( !(contentFile.exists() && contentFile.isFile()) ) throw new WTException("***** ERROR: Cannot upload,  file does not exist ****");

        wt.pom.Transaction trx = new wt.pom.Transaction();
		java.io.FileInputStream is = new java.io.FileInputStream(contentFile);
		trx.start();

		ContentHolder contentcookie = ContentHelper.service.getContents(contentholder);
        ApplicationData existAppData;
		ApplicationData newAppData = ApplicationData.newApplicationData(contentcookie);
		newAppData.setFileName( contentFile.getName() );

		Vector appdata = ContentHelper.getApplicationData(contentcookie);
		for (int i=0;i<appdata.size();i++){
			existAppData = (ApplicationData)appdata.get(i);
			if ( (existAppData.getFileName()).equals( contentFile.getName()) ){
				if (VERBOSE) System.out.println("secondary content already exists, replacing");
				newAppData=existAppData;
			}
		}
		//ApplicationData ContentServerHelper.service.updateAppData(ContentHolder contentholder, ApplicationData applicationdata, false)
		if (VERBOSE) System.out.println("uploading new content file " + contentFile.getAbsolutePath() );

		if (isPrimary){
			newAppData = wt.content.ContentServerHelper.service.updatePrimary( (FormatContentHolder)contentcookie, newAppData, is);
		}else{
			newAppData = wt.content.ContentServerHelper.service.updateContent( contentcookie, newAppData, (java.io.InputStream) is);
		}
		trx.commit();
	}

/**************************************************************************
 * Name:        getApplicationDataUrl
 *
 * Purpose:	upload content files from disk to contentholder's as primary or
 *          secondary (attachment).
 *
 **************************************************************************
*/
    public static URL getApplicationDataUrl(ContentHolder contentholder, String name)
        throws Exception
    {
		Vector<ApplicationData> attachements = new Vector<ApplicationData>();
		ContentHolder contentcookie = ContentHelper.service.getContents(contentholder);
        ApplicationData existAppData;
        URL url = null;
        if (VERBOSE) System.out.println ("Finding URL for: " + name);

		Vector appdata = ContentHelper.getApplicationData(contentcookie);
		for (int i=0;i<appdata.size();i++){
			existAppData = (ApplicationData)appdata.get(i);
			if ( (existAppData.getFileName()).equals(name) ){
				url = ContentHelper.getDownloadURL(contentholder, existAppData);
			}
		}
		return url;
	}

/**************************************************************************
 * Name:        getAIUrl
 *
 * Purpose:	upload content files from disk to contentholder's as primary or
 *          secondary (attachment).
 *
 **************************************************************************
*/
    public static URL getAIUrl(String name)
        throws Exception
    {
		return new URL("http://ldesrv8.sjtc.whirlpool.com/inapproval/user/" + name);
	}

/**************************************************************************
 * Name:        deleteContent
 *
 * Purpose:	given the file name of secondary content, will delete attachment
 *          from content holder
 *
 **************************************************************************
*/
    public static void deleteContent(String contentName, String contentholder)
        throws Exception
    {
		EPMDocument epmdoc = (EPMDocument)WpHelperUtility.getObject(contentholder);
		deleteContent(contentName,epmdoc);
	}

    public static void deleteContent(String contentName, ContentHolder contentholder)
        throws WTException, FileNotFoundException, java.beans.PropertyVetoException, IOException
    {
		ApplicationData existAppData;

		ContentHolder contentcookie = ContentHelper.service.getContents(contentholder);

		Vector appdata = ContentHelper.getApplicationData(contentcookie);
		for (int i=0;i<appdata.size();i++){
			existAppData = (ApplicationData)appdata.get(i);
			if ( (existAppData.getFileName()).equals( contentName) ){
				if (VERBOSE) System.out.println("deleting:" + contentName);
				PersistenceHelper.manager.delete(existAppData);
			}
		}
	}

  /**************************************************************************
   * Name:        addWatermark
   *
   * Purpose:	inserts a text watermark in the identified
   *
   **************************************************************************
   */
   public static File addWatermark(File pdfFile, String watermark) throws Exception {

		   File outFile = new File(genUniqueFile(pdfFile.getParent() ,"","$$$$.tmp"));
		   String outPath = pdfFile.getAbsolutePath();
		   if (VERBOSE) System.out.println("Processing PDF file: " + outPath);
		   FileInputStream is = new FileInputStream(pdfFile);
		   PdfReader reader = new PdfReader(is);
		   int n = reader.getNumberOfPages();
		   PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(outFile));

           BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
           PdfContentByte under;
           Rectangle pageSize;
           float x,y,fontSize;
           int i = 0;
           while (i < n) {
			   i++;
		       pageSize = reader.getPageSize(i);
		       y = pageSize.getHeight()/2;
		       x = pageSize.getWidth()/2;
		       fontSize = (x/(watermark.length()))*2;
		       if (VERBOSE) System.out.println ("Page size: X:" + x + "\tY:" + y);
			   under = stamp.getUnderContent(i);

			   PdfGState gstate = new PdfGState();
			   gstate.setFillOpacity(0.3f);
			   gstate.setStrokeOpacity(0.3f);
			   under.saveState();
			   under.setGState(gstate);

			   under.beginText();
			   under.setTextMatrix(30, 30);
			   under.setFontAndSize(bf, fontSize);
			   under.showTextAligned(Element.ALIGN_CENTER , watermark, x, y, 45);
			   under.endText();
			   under.restoreState();
		   }
		   stamp.close();
		   is.close();
		   if (VERBOSE) System.out.println("trying to delete " + pdfFile.getName() );
		   while (!pdfFile.delete()){
			   Thread.currentThread().sleep(SLEEP_INTERVAL);
			   if (VERBOSE) System.out.print(".");
			   if (i++>=MAX_PS_GEN) throw new WTException("***** ERROR: unable to add watermark exceeded allowable time limit ****");
		   }
           outFile.renameTo(new File(outPath));
		   return outFile;
   }

/**************************************************************************
 * Name:        removeDirectory
 *
 * Purpose:	recursively removes a file/directory and its contents.
 *
 **************************************************************************
 */
  public static void removeDirectory(File directory) throws Exception {
	  File[] contents = directory.listFiles();
	  File file;
	  boolean status = false;
	  int j=0;
	  if (VERBOSE) System.out.print( "\n" );
	  for (int i=0;i<contents.length;i++){
		  file = (File)contents[i];
		  if ( file.isDirectory() ) removeDirectory(file);
		  else if ( file.exists() ) {
			  if (VERBOSE) System.out.print( "DELETING: " + file.getAbsolutePath() );
			  status = file.delete();
			  while ( file.exists() ){
				  status = file.delete();
				  Thread.currentThread().sleep(SLEEP_INTERVAL);
				  if (j++>=MAX_PS_GEN) throw new WTException("***** ERROR: File deletion exceeded allowable time limit ****");
			  }
			  if (VERBOSE) System.out.print( status?"...DELETED\n":"...UNABLE TO DELETE\n" );
	      }
	  }
	  if (VERBOSE) System.out.print( "DELETING: " + directory.getAbsolutePath() );
	  status = directory.delete();
	  if (VERBOSE) System.out.print( status?"...DELETED\n":"...UNABLE TO DELETE\n" );

  }

/**************************************************************************
 * Name:        genUniqueFile
 *
 * Purpose:	include a random number in a file name to ensure it is unique
 *          .
 **************************************************************************
 */
  	public static synchronized String genUniqueFile(String absolutePath,String pattern,String file_name){
  		Random r = new Random();
  		File filename = new File(absolutePath, pattern + "_" + r.nextInt(5000)+ "_" + file_name);
  		if(filename.exists()){
  			return genUniqueFile(absolutePath,pattern,file_name);
  		}else{
  			return filename.getName();
  		}
	}

	private static class PSFilter implements FilenameFilter {
		String ext;
		public PSFilter(String ext) {
			this.ext = "." + ext;
		}
	    public boolean accept(File dir, String name) {
	        return (name.endsWith(ext));
	    }
    }

/**************************************************************************
 * Name:        getUnPubCount
 *
 * Purpose:	.
 *
 **************************************************************************
*/
  public static int getUnPubCount (PromotionNotice promotionNotice) throws Exception {
      int drwCount = 0;
      boolean exists;
      Representation representation;
      Baselineable member;
      QueryResult reps;

      String pnid = "" + wt.fc.PersistenceHelper.getObjectIdentifier(promotionNotice);
      QueryResult pnMembers = MaturityHelper.service.getBaselineItems(promotionNotice);
      while (pnMembers.hasMoreElements()) {
		  exists = false;
		  member = (Baselineable)pnMembers.nextElement();
		  if (member instanceof EPMDocument && ( ((EPMDocument)member).getDocType().toString()).equals("CADDRAWING")){
			  reps = RepresentationHelper.service.getRepresentations((Representable)member);
			  while (reps.hasMoreElements()) {
				  representation = (Representation)reps.nextElement();
				  if ((representation.getName()).equals(pnid)) exists = true;
			  }
			  if (!exists)drwCount++;
		  }
	  }
	  return drwCount;
  }

/**************************************************************************
 * Name:        isDrwPublished
 *
 * Purpose:	.
 *
 **************************************************************************
*/
  public static boolean isInPN (HolderToContent targetObject, PromotionNotice promotionNotice) throws Exception {
	  boolean isInPN = false;
	  ContentHolder contentholder = targetObject.getContentHolder();
	  if (contentholder instanceof DerivedImage){
		  DerivedImage rep =(DerivedImage)contentholder;
		  Persistable persistable = (rep.getDerivedFromReference()).getObject();
		  if ( persistable instanceof wt.epm.EPMDocument && MaturityHelper.service.isInBaseline((Promotable)persistable, promotionNotice) ){
	  		isInPN = true;
		  }
	  }
	  if (VERBOSE) System.out.println("***** HOLDER TO CONTENT IN PN: " + isInPN );
	  return isInPN;
  }


/**************************************************************************
 * Name:        getDrawings
 *
 * Purpose:	.Returns vector of EPMDocuments of type drw from a given Promotion
 *           Notice
 *
 **************************************************************************
*/
  public static Vector getDrawings (PromotionNotice promotionNotice) throws Exception {
      Vector<EPMDocument> drawings = new Vector<EPMDocument>();
      QueryResult pnMembers = MaturityHelper.service.getBaselineItems(promotionNotice);
      while (pnMembers.hasMoreElements()) {
		  Baselineable member = (Baselineable)pnMembers.nextElement();
		  if (member instanceof EPMDocument && ( ((EPMDocument)member).getDocType().toString()).equals("CADDRAWING")){
			  drawings.add((EPMDocument)member);
		  }
	  }
	  return drawings;
  }


/**************************************************************************
 * Name:        isPublished
 *
 * Purpose:	.
 *
 **************************************************************************
*/
  public static boolean isPublished (EPMDocument epmdoc, String name) throws Exception {
	  Vector<EPMDocument> drawings = new Vector<EPMDocument>(1);
	  drawings.add(epmdoc);
	  return isPublished(drawings,name);
  }
  public static boolean isPublished (Vector drawings, String name) throws Exception {
      int drwCount = drawings.size();
      int publishedCount = 0;
      String repName;
      boolean published = false;
      Representation representation;
      for (int i=0;i<drawings.size();i++){
		  if (VERBOSE) System.out.println("**** Checking for representation " + name);
		  QueryResult reps = RepresentationHelper.service.getRepresentations((Representable)drawings.get(i));
		  while (reps.hasMoreElements()) {
			  representation = (Representation)reps.nextElement();
			  repName = representation.getName();
			  if (repName.equals(name)){
				  ContentHolder contentcookie = ContentHelper.service.getContents(representation);
				  ContentRoleType role = ContentRoleType.toContentRoleType("SECONDARY");
				  QueryResult repSecondary =  ContentHelper.service.getContentsByRole(representation, role);
				  while (repSecondary.hasMoreElements()) {
					  ApplicationData appdata = (ApplicationData)repSecondary.nextElement();
					  repName = appdata.getFileName();
					  if (repName.endsWith(".pdf")) publishedCount++;
				  }
			  }
		  }
	  }
	  return (drwCount==publishedCount);
  }

}